﻿using System;
using System.Data.Odbc;
using System.Linq;
using System.Text;

namespace Centido.Core
{
    /// <summary>
    ///   A set of table read operations.
    /// </summary>
    public static class TableReadOperations
    {
        /// <summary>
        ///   Reads a row with the specified id.
        /// </summary>
        /// <param name="table">The target table.</param>
        /// <param name = "rowId">The id of the row.</param>
        /// <param name = "source">The target data source.</param>
        /// <param name = "values">The list of the column values to be read.</param>
        /// <returns>Returns true if a row with the specified id was found; otherwise, false.</returns>
        private static bool ReadRowImplementation(Table table, Guid rowId,
            DataSource source, params ColumnValue[] values)
        {
            // If there is nothing to read
            if (values == null || values.Length == 0)
                throw new ArgumentException("No table columns to be read were specified.");

            var tableName = table.GetDataSourceName(source);

            // List of comma separated column names
            var columnList = new StringBuilder();

            values.ToList().ForEach(
                pair => columnList.Append(columnList.Length != 0 ? "," : string.Empty).Append(pair.ColumnName));

            // Row was found
            var rowWasFound = false;

            table.Domain.DbAccess.ExecuteReader(
                cmd =>
                {
                    cmd.CommandText = string.Format(
                        "select {0} from {1} where {2}=?",
                        columnList,
                        tableName,
                        Db.Tables.UserTable.Col.Id);

                    cmd.Parameters.Add(table.Domain.DbAccess.CreateParameter("id", OdbcType.Binary, rowId.ToByteArray()));
                },
                reader =>
                {
                    rowWasFound = true;

                    foreach (var value in values.ToList())
                        value.Value = reader[value.ColumnName];
                });

            return rowWasFound;
        }


        /// <summary>
        ///   Reads the columns of the specified table row.   
        /// </summary>
        /// <remarks>
        /// <para>The current user has to have the select permission on this table in order to execute this function.</para>
        /// </remarks>
        /// <param name="table">The target table.</param>
        /// <param name = "rowId">The id of the specified row.</param>
        /// <param name = "values">The list of columns to be read.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when one of the specified values of the table columns is null.</exception>
        /// <exception cref="ArgumentException">Thrown when no table columns were specified.</exception>
        /// <exception cref="System.Data.Common.DbException">Thrown when the current user has no permission to read from the table.</exception>
        /// <returns>Returns true if a row with the specified id was found; otherwise, false.</returns>
        /// <example>
        ///   The following example shows how to read the values in the system columns.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///             
        ///             domain.DeleteAll();
        ///             
        ///             //Creating a new table
        ///             var table = domain.AddTable("my_table");
        /// 
        ///             //Granting insert and select permissions
        ///             table.Grant(DataAction.Insert);
        ///             table.Grant(DataAction.Select);
        /// 
        ///             //Inserting a new row
        ///             var id = table.InsertRow();
        /// 
        ///             //Reading the values of the system columns
        ///             var rowOwner = table.SystemColumns.Owner.EmptyValue();
        ///             var createdBy = table.SystemColumns.CreatedBy.EmptyValue();
        ///             var createdOn = table.SystemColumns.CreatedOn.EmptyValue();
        ///             var updatedBy = table.SystemColumns.UpdatedBy.EmptyValue();
        ///             var updatedOn = table.SystemColumns.UpdatedOn.EmptyValue();
        ///             var rowVersion = table.SystemColumns.RowVersion.EmptyValue();
        /// 
        ///             table.ReadRow(id, rowOwner, createdBy, createdOn, updatedBy,
        ///                 updatedOn, rowVersion);
        /// 
        ///             Console.WriteLine("Id = {0}, owner = {1}, created by = {2}"
        ///             + " created on = {3}, updated by = {4}, updated on = {5}, version = {6}",
        ///             id,
        ///             DbValue.GetString(rowOwner),
        ///             DbValue.GetString(createdBy),
        ///             DbValue.GetDateTime(createdOn),
        ///             DbValue.GetString(updatedBy),
        ///             DbValue.GetDateTime(updatedOn),
        ///             DbValue.GetGuid(rowVersion));
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool ReadRow(this Table table, Guid rowId, params ColumnValue[] values)
        {
            if (table == null)
                throw new ArgumentNullException("table");

            return ReadRowImplementation(table, rowId, DataSource.UserView, values);
        }


        /// <summary>
        ///   Reads the columns of the specified table row from the specified data source.   
        /// </summary>
        /// <remarks>
        /// <para>The current user has to have the select permission on the specified data source in order to execute this function.</para>
        /// </remarks>
        /// <param name="table">The target table.</param>
        /// <param name = "rowId">The id of the specified row.</param>
        /// <param name="source">The source of the rows.</param>
        /// <param name = "values">The list of columns to be read.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when one of the specified values of the table columns is null.</exception>
        /// <exception cref="ArgumentException">Thrown when no table columns were specified.</exception>
        /// <exception cref="System.Data.Common.DbException">Thrown when the current user has no permission to read from the table.</exception>
        /// <returns>Returns true if a row with the specified id was found; otherwise, false.</returns>
        /// <example>
        ///   The following example shows how to read the values in the system columns.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///             
        ///             domain.DeleteAll();
        ///             
        ///             //Creating a new table
        ///             var table = domain.AddTable("my_table");    
        /// 
        ///             //Granting insert and select permissions
        ///             table.Grant(DataAction.Insert);
        ///             table.Grant(DataAction.Select);
        /// 
        ///             //Inserting a new row
        ///             var id = table.InsertRow();
        /// 
        ///             //Reading the values of the system columns
        ///             var rowOwner = table.SystemColumns.Owner.EmptyValue();
        ///             var createdBy = table.SystemColumns.CreatedBy.EmptyValue();
        ///             var createdOn = table.SystemColumns.CreatedOn.EmptyValue();
        ///             var updatedBy = table.SystemColumns.UpdatedBy.EmptyValue();
        ///             var updatedOn = table.SystemColumns.UpdatedOn.EmptyValue();
        ///             var rowVersion = table.SystemColumns.RowVersion.EmptyValue();
        /// 
        ///             table.ReadRow(id, DataSource.UserView, rowOwner, createdBy, createdOn, updatedBy,
        ///                 updatedOn, rowVersion);
        /// 
        ///             Console.WriteLine("Id = {0}, owner = {1}, created by = {2}"
        ///             + " created on = {3}, updated by = {4}, updated on = {5}, version = {6}",
        ///             id,
        ///             DbValue.GetString(rowOwner),
        ///             DbValue.GetString(createdBy),
        ///             DbValue.GetDateTime(createdOn),
        ///             DbValue.GetString(updatedBy),
        ///             DbValue.GetDateTime(updatedOn),
        ///             DbValue.GetGuid(rowVersion));
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool ReadRow(this Table table, Guid rowId,
            DataSource source, params ColumnValue[] values)
        {
            if (table == null)
                throw new ArgumentNullException("table");

            return ReadRowImplementation(table, rowId, source, values);
        }
    }
}
